{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<a href=\"https://colab.research.google.com/github/jeffheaton/t81_558_deep_learning/blob/master/t81_558_class_13_01_flask.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# T81-558: Applications of Deep Neural Networks\n",
    "**Module 13: Advanced/Other Topics**\n",
    "* Instructor: [Jeff Heaton](https://sites.wustl.edu/jeffheaton/), McKelvey School of Engineering, [Washington University in St. Louis](https://engineering.wustl.edu/Programs/Pages/default.aspx)\n",
    "* For more information visit the [class website](https://sites.wustl.edu/jeffheaton/t81-558/)."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Module 13 Video Material\n",
    "\n",
    "* **Part 13.1: Flask and Deep Learning Web Services** [[Video]](https://www.youtube.com/watch?v=H73m9XvKHug&list=PLjy4p-07OYzulelvJ5KVaT2pDlxivl_BN) [[Notebook]](https://github.com/jeffheaton/t81_558_deep_learning/blob/master/t81_558_class_13_01_flask.ipynb)\n",
    "* Part 13.2: Interrupting and Continuing Training  [[Video]](https://www.youtube.com/watch?v=kaQCdv46OBA&list=PLjy4p-07OYzulelvJ5KVaT2pDlxivl_BN) [[Notebook]](https://github.com/jeffheaton/t81_558_deep_learning/blob/master/t81_558_class_13_02_checkpoint.ipynb)\n",
    "* Part 13.3: Using a Keras Deep Neural Network with a Web Application  [[Video]](https://www.youtube.com/watch?v=OBbw0e-UroI&list=PLjy4p-07OYzulelvJ5KVaT2pDlxivl_BN) [[Notebook]](https://github.com/jeffheaton/t81_558_deep_learning/blob/master/t81_558_class_13_03_web.ipynb)\n",
    "* Part 13.4: When to Retrain Your Neural Network [[Video]](https://www.youtube.com/watch?v=K2Tjdx_1v9g&list=PLjy4p-07OYzulelvJ5KVaT2pDlxivl_BN) [[Notebook]](https://github.com/jeffheaton/t81_558_deep_learning/blob/master/t81_558_class_13_04_retrain.ipynb)\n",
    "* Part 13.5: Tensor Processing Units (TPUs)  [[Video]](https://www.youtube.com/watch?v=Ygyf3NUqvSc&list=PLjy4p-07OYzulelvJ5KVaT2pDlxivl_BN) [[Notebook]](https://github.com/jeffheaton/t81_558_deep_learning/blob/master/t81_558_class_13_05_tpu.ipynb)\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Part 13.1: Flask and Deep Learning Web Services\n",
    "\n",
    "Suppose you would like to create websites based on neural networks. In that case, we must expose the neural network so that Python and other programming languages can efficiently execute. The usual means for such integration is a web service. One of the most popular libraries for doing this in Python is [Flask](https://palletsprojects.com/p/flask/). This library allows you to quickly deploy your Python applications, including TensorFlow, as web services.\n",
    "\n",
    "Neural network deployment is a complex process, usually carried out by a company's [Information Technology (IT) group](https://en.wikipedia.org/wiki/Information_technology). When large numbers of clients must access your model, scalability becomes essential. The cloud usually handles this. The designers of Flask did not design for high-volume systems. When deployed to production, you will wrap models in [Gunicorn](https://gunicorn.org/) or TensorFlow Serving. We will discuss high-volume cloud deployment in the next section. Everything presented in this part ith Flask is directly compatible with the higher volume Gunicorn system. When early in the development process, it is common to use Flask directly.\n",
    "\n",
    "## Flask Hello World\n",
    "\n",
    "Flask is the server, and Jupyter usually fills the role of the client. It is uncommon to run Flask from a Jupyter notebook. However, we can run a simple web service from Jupyter. We will quickly move beyond this and deploy using a Python script (.py). Because we must use .py files, it won't be easy to use Google CoLab, as you will be running from the command line. For now, let's execute a Flask web container in Jupyter."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      " * Running on http://localhost:9000/ (Press CTRL+C to quit)\n",
      "127.0.0.1 - - [01/Aug/2019 14:51:25] \"\u001b[37mGET / HTTP/1.1\u001b[0m\" 200 -\n",
      "127.0.0.1 - - [01/Aug/2019 14:51:25] \"\u001b[33mGET /favicon.ico HTTP/1.1\u001b[0m\" 404 -\n"
     ]
    }
   ],
   "source": [
    "from werkzeug.wrappers import Request, Response\n",
    "from flask import Flask\n",
    "\n",
    "app = Flask(__name__)\n",
    "\n",
    "@app.route(\"/\")\n",
    "def hello():\n",
    "    return \"Hello World!\"\n",
    "\n",
    "if __name__ == '__main__':\n",
    "    from werkzeug.serving import run_simple\n",
    "    run_simple('localhost', 9000, app)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "This program starts a web service on port 9000 of your computer.  This cell will remain running (appearing locked up).  However, it is merely waiting for browsers to connect.  If you point your browser at the following URL, you will interact with the Flask web service.\n",
    "\n",
    "* http://localhost:9000/\n",
    "\n",
    "You should see Hello World displayed.\n",
    "\n",
    "## MPG Flask\n",
    "\n",
    "Usually, you will interact with a web service through JSON.  A program will send a JSON message to your Flask application, and your Flask application will return a JSON.  Later, in module 13.3, we will see how to attach this web service to a web application that you can interact with through a browser.  We will create a Flask wrapper for a neural network that predicts the miles per gallon.  The sample JSON will look like this.\n",
    "\n",
    "```\n",
    "{\n",
    "  \"cylinders\": 8, \n",
    "  \"displacement\": 300,\n",
    "  \"horsepower\": 78, \n",
    "  \"weight\": 3500,\n",
    "  \"acceleration\": 20, \n",
    "  \"year\": 76,\n",
    "  \"origin\": 1\n",
    "}\n",
    "```\n",
    "\n",
    "We will see two different means of POSTing this JSON data to our web server.  First, we will use a utility called [POSTman](https://www.getpostman.com/).  Secondly, we will use Python code to construct the JSON message and interact with Flask. \n",
    "\n",
    "First, it is necessary to train a neural network with the MPG dataset.  This technique is very similar to what we've done many times before.  However, we will save the neural network so that we can load it later.  We do not want to have Flask train the neural network.  We wish to have the neural network already trained and deploy the already prepared .H5 file to save the neural network.  The following code trains an MPG neural network."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "WARNING: Logging before flag parsing goes to stderr.\n",
      "W0801 14:54:30.408809 140735710749568 deprecation.py:323] From /Users/jheaton/miniconda3/envs/tensorflow/lib/python3.6/site-packages/tensorflow_core/python/keras/optimizer_v2/optimizer_v2.py:468: BaseResourceVariable.constraint (from tensorflow.python.ops.resource_variable_ops) is deprecated and will be removed in a future version.\n",
      "Instructions for updating:\n",
      "Apply a constraint manually following the optimizer update step.\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Train on 298 samples, validate on 100 samples\n",
      "Epoch 1/1000\n",
      "298/298 - 0s - loss: 2009.9526 - val_loss: 1452.2106\n",
      "Epoch 2/1000\n",
      "298/298 - 0s - loss: 562.4718 - val_loss: 532.2283\n",
      "Epoch 3/1000\n",
      "298/298 - 0s - loss: 331.3064 - val_loss: 229.2169\n",
      "Epoch 4/1000\n",
      "298/298 - 0s - loss: 197.1110 - val_loss: 149.5195\n",
      "Epoch 5/1000\n",
      "298/298 - 0s - loss: 167.2275 - val_loss: 137.9593\n",
      "Epoch 6/1000\n",
      "298/298 - 0s - loss: 141.5222 - val_loss: 123.4302\n",
      "Epoch 7/1000\n",
      "298/298 - 0s - loss: 134.0808 - val_loss: 119.0394\n",
      "Epoch 8/1000\n",
      "298/298 - 0s - loss: 128.3394 - val_loss: 116.1934\n",
      "Epoch 9/1000\n",
      "298/298 - 0s - loss: 125.9083 - val_loss: 111.8186\n",
      "Epoch 10/1000\n",
      "298/298 - 0s - loss: 122.4650 - val_loss: 108.0787\n",
      "Epoch 11/1000\n",
      "298/298 - 0s - loss: 123.8484 - val_loss: 117.2898\n",
      "Epoch 12/1000\n",
      "298/298 - 0s - loss: 122.1327 - val_loss: 102.7727\n",
      "Epoch 13/1000\n",
      "298/298 - 0s - loss: 114.5446 - val_loss: 97.1289\n",
      "Epoch 14/1000\n",
      "298/298 - 0s - loss: 109.9774 - val_loss: 104.2486\n",
      "Epoch 15/1000\n",
      "298/298 - 0s - loss: 103.9899 - val_loss: 97.3281\n",
      "Epoch 16/1000\n",
      "298/298 - 0s - loss: 106.6320 - val_loss: 94.8660\n",
      "Epoch 17/1000\n",
      "298/298 - 0s - loss: 99.9431 - val_loss: 87.0038\n",
      "Epoch 18/1000\n",
      "298/298 - 0s - loss: 99.5071 - val_loss: 84.3153\n",
      "Epoch 19/1000\n",
      "298/298 - 0s - loss: 97.8698 - val_loss: 76.5715\n",
      "Epoch 20/1000\n",
      "298/298 - 0s - loss: 89.4999 - val_loss: 85.0211\n",
      "Epoch 21/1000\n",
      "298/298 - 0s - loss: 93.6648 - val_loss: 77.6890\n",
      "Epoch 22/1000\n",
      "298/298 - 0s - loss: 84.4505 - val_loss: 67.5825\n",
      "Epoch 23/1000\n",
      "298/298 - 0s - loss: 80.5943 - val_loss: 65.0318\n",
      "Epoch 24/1000\n",
      "298/298 - 0s - loss: 84.3871 - val_loss: 71.8327\n",
      "Epoch 25/1000\n",
      "298/298 - 0s - loss: 73.9898 - val_loss: 59.8741\n",
      "Epoch 26/1000\n",
      "298/298 - 0s - loss: 72.3061 - val_loss: 64.4955\n",
      "Epoch 27/1000\n",
      "298/298 - 0s - loss: 71.3641 - val_loss: 60.4624\n",
      "Epoch 28/1000\n",
      "298/298 - 0s - loss: 68.7283 - val_loss: 53.2123\n",
      "Epoch 29/1000\n",
      "298/298 - 0s - loss: 65.7615 - val_loss: 53.1017\n",
      "Epoch 30/1000\n",
      "298/298 - 0s - loss: 64.4437 - val_loss: 50.6363\n",
      "Epoch 31/1000\n",
      "298/298 - 0s - loss: 61.7922 - val_loss: 53.4028\n",
      "Epoch 32/1000\n",
      "298/298 - 0s - loss: 60.4436 - val_loss: 45.5426\n",
      "Epoch 33/1000\n",
      "298/298 - 0s - loss: 58.2074 - val_loss: 44.6183\n",
      "Epoch 34/1000\n",
      "298/298 - 0s - loss: 58.3198 - val_loss: 42.4181\n",
      "Epoch 35/1000\n",
      "298/298 - 0s - loss: 55.2720 - val_loss: 41.4758\n",
      "Epoch 36/1000\n",
      "298/298 - 0s - loss: 54.0140 - val_loss: 39.8373\n",
      "Epoch 37/1000\n",
      "298/298 - 0s - loss: 52.7805 - val_loss: 50.7949\n",
      "Epoch 38/1000\n",
      "298/298 - 0s - loss: 52.7188 - val_loss: 38.0612\n",
      "Epoch 39/1000\n",
      "298/298 - 0s - loss: 49.0947 - val_loss: 37.0849\n",
      "Epoch 40/1000\n",
      "298/298 - 0s - loss: 48.5237 - val_loss: 35.5431\n",
      "Epoch 41/1000\n",
      "298/298 - 0s - loss: 47.1861 - val_loss: 34.2021\n",
      "Epoch 42/1000\n",
      "298/298 - 0s - loss: 45.6822 - val_loss: 37.6906\n",
      "Epoch 43/1000\n",
      "298/298 - 0s - loss: 44.9434 - val_loss: 34.6666\n",
      "Epoch 44/1000\n",
      "298/298 - 0s - loss: 43.5861 - val_loss: 32.9166\n",
      "Epoch 45/1000\n",
      "298/298 - 0s - loss: 43.2145 - val_loss: 39.4031\n",
      "Epoch 46/1000\n",
      "298/298 - 0s - loss: 42.4890 - val_loss: 30.3377\n",
      "Epoch 47/1000\n",
      "298/298 - 0s - loss: 41.9875 - val_loss: 29.8683\n",
      "Epoch 48/1000\n",
      "298/298 - 0s - loss: 40.9617 - val_loss: 30.6645\n",
      "Epoch 49/1000\n",
      "298/298 - 0s - loss: 40.4144 - val_loss: 34.1248\n",
      "Epoch 50/1000\n",
      "298/298 - 0s - loss: 40.4761 - val_loss: 33.8365\n",
      "Epoch 51/1000\n",
      "298/298 - 0s - loss: 39.0555 - val_loss: 31.4981\n",
      "Epoch 52/1000\n",
      "Restoring model weights from the end of the best epoch.\n",
      "298/298 - 0s - loss: 37.9472 - val_loss: 32.6139\n",
      "Epoch 00052: early stopping\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "<tensorflow.python.keras.callbacks.History at 0x12df96208>"
      ]
     },
     "execution_count": 2,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from tensorflow.keras.models import Sequential\n",
    "from tensorflow.keras.layers import Dense, Activation\n",
    "from sklearn.model_selection import train_test_split\n",
    "from tensorflow.keras.callbacks import EarlyStopping\n",
    "import pandas as pd\n",
    "import io\n",
    "import os\n",
    "import requests\n",
    "import numpy as np\n",
    "from sklearn import metrics\n",
    "\n",
    "df = pd.read_csv(\n",
    "    \"https://data.heatonresearch.com/data/t81-558/auto-mpg.csv\", \n",
    "    na_values=['NA', '?'])\n",
    "\n",
    "cars = df['name']\n",
    "\n",
    "# Handle missing value\n",
    "df['horsepower'] = df['horsepower'].fillna(df['horsepower'].median())\n",
    "\n",
    "# Pandas to Numpy\n",
    "x = df[['cylinders', 'displacement', 'horsepower', 'weight',\n",
    "       'acceleration', 'year', 'origin']].values\n",
    "y = df['mpg'].values # regression\n",
    "\n",
    "# Split into validation and training sets\n",
    "x_train, x_test, y_train, y_test = train_test_split(    \n",
    "    x, y, test_size=0.25, random_state=42)\n",
    "\n",
    "# Build the neural network\n",
    "model = Sequential()\n",
    "model.add(Dense(25, input_dim=x.shape[1], activation='relu')) # Hidden 1\n",
    "model.add(Dense(10, activation='relu')) # Hidden 2\n",
    "model.add(Dense(1)) # Output\n",
    "model.compile(loss='mean_squared_error', optimizer='adam')\n",
    "\n",
    "monitor = EarlyStopping(monitor='val_loss', min_delta=1e-3, patience=5, \\\n",
    "        verbose=1, mode='auto',\\\n",
    "        restore_best_weights=True)\n",
    "model.fit(x_train,y_train,validation_data=(x_test,y_test),\\\n",
    "          callbacks=[monitor],verbose=2,epochs=1000)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Next, we evaluate the score.  This evaluation is more of a sanity check to ensure the code above worked as expected. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "After load score (RMSE): 5.465193688130732\n"
     ]
    }
   ],
   "source": [
    "pred = model.predict(x_test)\n",
    "# Measure RMSE error.  RMSE is common for regression.\n",
    "score = np.sqrt(metrics.mean_squared_error(pred,y_test))\n",
    "print(f\"After load score (RMSE): {score}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Next, we save the neural network to a .H5 file."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "model.save(os.path.join(\"./dnn/\",\"mpg_model.h5\"))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We want the Flask web service to check that the input JSON is valid.  To do this, we need to know what values we expect and their logical ranges.  The following code outputs the expected fields and their ranges, and packages all of this information into a JSON object that you should copy to the Flask web application.  This code allows us to validate the incoming JSON requests."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{\n",
      "\"cylinders\":{\"min\":3,\"max\":8},\n",
      "\"displacement\":{\"min\":68.0,\"max\":455.0},\n",
      "\"horsepower\":{\"min\":46.0,\"max\":230.0},\n",
      "\"weight\":{\"min\":1613,\"max\":5140},\n",
      "\"acceleration\":{\"min\":8.0,\"max\":24.8},\n",
      "\"year\":{\"min\":70,\"max\":82},\n",
      "\"origin\":{\"min\":1,\"max\":3}\n",
      "}\n"
     ]
    }
   ],
   "source": [
    "cols = [x for x in df.columns if x not in ('mpg','name')]\n",
    "\n",
    "print(\"{\")\n",
    "for i,name in enumerate(cols):\n",
    "    print(f'\"{name}\":{{\"min\":{df[name].min()},\\\n",
    "          \"max\":{df[name].max()}}}{\",\" if i<(len(cols)-1) else \"\"}')\n",
    "print(\"}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Finally, we set up the Python code to call the model for a single car and get a prediction.  You should also copy this code to the Flask web application."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "6.212100505828857"
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "import os\n",
    "from tensorflow.keras.models import load_model\n",
    "import numpy as np\n",
    "\n",
    "model = load_model(os.path.join(\"./dnn/\",\"mpg_model.h5\"))\n",
    "x = np.zeros( (1,7) )\n",
    "\n",
    "x[0,0] = 8 # 'cylinders', \n",
    "x[0,1] = 400 # 'displacement', \n",
    "x[0,2] = 80 # 'horsepower', \n",
    "x[0,3] = 2000 # 'weight',\n",
    "x[0,4] = 19 # 'acceleration', \n",
    "x[0,5] = 72 # 'year', \n",
    "x[0,6] = 1 # 'origin'\n",
    "\n",
    "\n",
    "pred = model.predict(x)\n",
    "float(pred[0])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The completed web application can be found here:\n",
    "    \n",
    "* [mpg_server_1.py](./py/mpg_server_1.py)\n",
    "\n",
    "You can run this server from the command line with the following command:\n",
    "\n",
    "```\n",
    "python mpg_server_1.py\n",
    "```\n",
    "\n",
    "If you are using a virtual environment (described in Module 1.1), use the ```activate tensorflow``` command for Windows or ```source activate tensorflow``` for Mac before executing the above command.\n",
    "\n",
    "## Flask MPG Client\n",
    "\n",
    "Now that we have a web service running, we would like to access it.  This server is a bit more complicated than the \"Hello World\" web server we first saw in this part.  The request to display was an HTTP GET.  We must now do an HTTP POST.  To accomplish access to a web service, you must use a client.  We will see how to use [PostMan](https://www.getpostman.com/) and directly through a Python program in Jupyter.\n",
    "\n",
    "We will begin with PostMan.  If you have not already done so, install PostMan.  \n",
    "\n",
    "To successfully use PostMan to query your web service, you must enter the following settings:\n",
    "\n",
    "* POST Request to http://localhost:5000/api/mpg\n",
    "* RAW JSON and paste in JSON from above\n",
    "* Click Send and you should get a correct result\n",
    "\n",
    "Figure 13.PM shows a successful result.\n",
    "\n",
    "**Figure 13.PM: PostMan JSON**\n",
    "![PostMan JSON](https://raw.githubusercontent.com/jeffheaton/t81_558_deep_learning/master/images/postman-1.png \"PostMan JSON\")\n",
    "\n",
    "This same process can be done programmatically in Python."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Success: {\n",
      "  \"errors\": [], \n",
      "  \"id\": \"643d027e-554f-4401-ba5f-78592ae7e070\", \n",
      "  \"mpg\": 23.885438919067383\n",
      "}\n",
      "\n"
     ]
    }
   ],
   "source": [
    "import requests\n",
    "\n",
    "json = {\n",
    "  \"cylinders\": 8, \n",
    "  \"displacement\": 300,\n",
    "  \"horsepower\": 78, \n",
    "  \"weight\": 3500,\n",
    "  \"acceleration\": 20, \n",
    "  \"year\": 76,\n",
    "  \"origin\": 1\n",
    "}\n",
    "\n",
    "r = requests.post(\"http://localhost:5000/api/mpg\",json=json)\n",
    "if r.status_code == 200:\n",
    "    print(\"Success: {}\".format(r.text))\n",
    "else: print(\"Failure: {}\".format(r.text))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Images and Web Services\n",
    "\n",
    "We can also accept images from web services. We will create a web service that accepts images and classifies them using MobileNet. You will follow the same process; load your network as we did for the MPG example. You can find the completed web service can here:\n",
    "\n",
    "[image_server_1.py](./py/image_server_1.py)\n",
    "\n",
    "You can run this server from the command line with:\n",
    "\n",
    "```\n",
    "python mpg_server_1.py\n",
    "```\n",
    "\n",
    "If you are using a virtual environment (described in Module 1.1), use the ```activate tensorflow``` command for Windows or ```source activate tensorflow``` for Mac before executing the above command.\n",
    "\n",
    "To successfully use PostMan to query your web service, you must enter the following settings:\n",
    "\n",
    "* POST Request to http://localhost:5000/api/image\n",
    "* Use \"Form Data\" and create one entry named \"image\" that is a file. Choose an image file to classify.\n",
    "* Click Send, and you should get a correct result\n",
    "\n",
    "Figure 13.PMI shows a successful result.\n",
    "\n",
    "**Figure 13.PMI: PostMan Images**\n",
    "![PostMan Image](https://raw.githubusercontent.com/jeffheaton/t81_558_deep_learning/master/images/postman-2.png \"PostMan Image\")\n",
    "\n",
    "This same process can be done programmatically in Python."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Success: {\n",
      "  \"pred\": [\n",
      "    {\n",
      "      \"name\": \"boxer\", \n",
      "      \"prob\": 0.9178281426429749\n",
      "    }, \n",
      "    {\n",
      "      \"name\": \"American_Staffordshire_terrier\", \n",
      "      \"prob\": 0.04458194971084595\n",
      "    }, \n",
      "    {\n",
      "      \"name\": \"French_bulldog\", \n",
      "      \"prob\": 0.018736232072114944\n",
      "    }, \n",
      "    {\n",
      "      \"name\": \"bull_mastiff\", \n",
      "      \"prob\": 0.016469275578856468\n",
      "    }, \n",
      "    {\n",
      "      \"name\": \"pug\", \n",
      "      \"prob\": 0.0009862519800662994\n",
      "    }\n",
      "  ]\n",
      "}\n",
      "\n"
     ]
    }
   ],
   "source": [
    "import requests\n",
    "response = requests.post('http://localhost:5000/api/image', files=\\\n",
    "        dict(image=('hickory.jpeg',open('./photos/hickory.jpeg','rb'))))\n",
    "if response.status_code == 200:\n",
    "    print(\"Success: {}\".format(response.text))\n",
    "else: print(\"Failure: {}\".format(response.text))"
   ]
  }
 ],
 "metadata": {
  "anaconda-cloud": {},
  "kernelspec": {
   "display_name": "Python 3.9 (tensorflow)",
   "language": "python",
   "name": "tensorflow"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.9.7"
  },
  "varInspector": {
   "cols": {
    "lenName": 16,
    "lenType": 16,
    "lenVar": 40
   },
   "kernels_config": {
    "python": {
     "delete_cmd_postfix": "",
     "delete_cmd_prefix": "del ",
     "library": "var_list.py",
     "varRefreshCmd": "print(var_dic_list())"
    },
    "r": {
     "delete_cmd_postfix": ") ",
     "delete_cmd_prefix": "rm(",
     "library": "var_list.r",
     "varRefreshCmd": "cat(var_dic_list()) "
    }
   },
   "types_to_exclude": [
    "module",
    "function",
    "builtin_function_or_method",
    "instance",
    "_Feature"
   ],
   "window_display": false
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
